﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.Serialization;

namespace OpenWaves.ActivityFeed
{
    [DataContract]
    public class Update
    {
        [DataMember]
        [SuppressMessageAttribute("Microsoft.Performance", "CA1811:Avoid uncalled private code")]
        public string Id { get; private set; }

        [DataMember]
        public string PublishedBy { get; private set; }

        [DataMember]
        public DateTime PublishedOn { get; private set; }

        [DataMember]
        public string Source { get; private set; }

        [DataMember(Name = "Topics")]
        private readonly List<Topic> topics = new List<Topic>();

        public IEnumerable<Topic> Topics
        {
            get { return this.topics; }
        }

        [DataMember(Name = "Comments")]
        private IList<Comment> comments = new List<Comment>();

        public IEnumerable<Comment> Comments
        {
            get { return this.comments; }
        }

        [DataMember]
        [SuppressMessageAttribute("Microsoft.Usage", "CA2227:Collection properties should be read only")]
        [SuppressMessageAttribute("Microsoft.Naming", "CA1721:Property names should not match get methods")]
        public IList<LikeEntry> Likes { get; protected set; }

        [SuppressMessageAttribute("Microsoft.Design", "CA1024:Use properties where appropriate")]
        public IEnumerable<LikeEntry> GetLikes()
        {
            return this.Likes.OrderByDescending(l => l.CreatedDate);
        }

        [DataMember]
        public bool IsPublic { get; private set; }

        [DataMember(Name = "UserGroups")]
        private readonly List<UserGroup> userGroups = new List<UserGroup>();

        public IEnumerable<UserGroup> UserGroups
        {
            get { return this.userGroups; }
        }

        [DataMember]
        public UpdateContent Content { get; protected set; }

        protected Update()
        {
            this.Likes = new List<LikeEntry>();
        }

        public Update(UpdateContent content, string publisherId, string source, IEnumerable<Topic> topics, bool isPublic, IEnumerable<UserGroup> userGroups): this()
        {
            Contract.Requires<ArgumentNullException>(content != null);
            Contract.Requires<ArgumentNullException>(String.IsNullOrEmpty(publisherId) == false);
            Contract.Requires<ArgumentNullException>(topics != null);

            this.Content = content;
            this.PublishedOn = Time.Now;
            this.PublishedBy = publisherId;
            this.Source = source;
            this.topics.AddRange(topics.Distinct());

            this.IsPublic = isPublic;
            this.userGroups.AddRange(userGroups.Distinct());            
        }

        public Comment AddComment(string text, string publisherId)
        {
            Contract.Requires<ArgumentNullException>(String.IsNullOrEmpty(text) == false);
            Contract.Requires<ArgumentNullException>(String.IsNullOrEmpty(publisherId) == false);

            var comment = new Comment { Id = Guid.NewGuid().ToString(), PublishedBy = publisherId, Text = text, PublishedOn = Time.Now };
            this.comments.Add(comment);
            return comment;
        }

        public void RemoveComment(Comment comment)
        {
            Contract.Requires<ArgumentNullException>(comment != null);
            this.comments.Remove(comment);
        }

        public LikeEntry AddLike(string userId)
        {
            Contract.Requires<ArgumentNullException>(String.IsNullOrEmpty(userId) == false);

            var like = this.GetLikes().SingleOrDefault(l => l.UserId == userId);
            if (like != null)
                return like;

            like = new LikeEntry { CreatedDate = Time.Now, UserId = userId };
            this.Likes.Add(like);
            return like;
        }
        
        public void RemoveLike(string userId)
        {
            Contract.Requires<ArgumentNullException>(String.IsNullOrEmpty(userId) == false);
            var like = this.GetLikes().SingleOrDefault(l => l.UserId == userId);
            if (like == null)
                return;
            this.Likes.Remove(like);
        }

        public bool CanBeDeletedBy(string userId)
        {
            Contract.Requires<ArgumentNullException>(String.IsNullOrEmpty(userId) == false);

            return this.PublishedBy == userId;
        }
    }
}